package tp.pr4.maploader;

import java.io.IOException;
import java.io.InputStream;
import java.util.StringTokenizer;

import tp.pr4.Directions;
import tp.pr4.Door;
import tp.pr4.Map;
import tp.pr4.Room;
import tp.pr4.items.Food;
import tp.pr4.items.Item;
import tp.pr4.items.Key;
import tp.pr4.items.Valuable;
import tp.pr4.maploader.exceptions.WrongMapFormatException;

//Esta clase es mia. No la toques porfa.

public class MapLoaderFromTxtFile

{

	private String[] _lineas = null;// Es el fichero de texto en un array

	private int _BeginMap = 0;
	private int _BeginRooms = 1; // siempre debe ser así.

	// para saber si lo hemos encontrado ya o no (no deja usar null)

	private int _EndRooms = -1;
	private int _BeginDoors = -1;
	private int _EndDoors = -1;
	private int _BeginItems = -1;
	private int _EndItems = -1;
	private int _EndMap = -1;

	private Room[] _arrayRooms = null;
	private Door[] _arrayDoors = null;

	private Map _mapa;

	public MapLoaderFromTxtFile()
	{

	}
/*
 * 
 * 		item = new Key(id, description, this._mapa.getDoor(this._arrayDoors[Integer.valueOf(tkn
					.nextToken())]));
 */
	public Map loadMap(InputStream file) throws IOException
	{
		 _mapa = null;

		String mapaString = loadInputStreamInString(file);
		this._lineas = mapaString.split("\n");
		this.mapIndicatorSetter();
try {
			this.fillArrayRooms();
			this._arrayDoors = new Door[this._EndDoors - this._BeginDoors];
			
			int expectedNumber = 0;
			_mapa = new Map(this._arrayRooms[0]);
			for (int i = this._BeginDoors + 1; i < _EndDoors; i++) {
				Door aux=createDoorFromLine(this._lineas[i], expectedNumber);
				this._arrayDoors[expectedNumber++]=aux;
				_mapa.addDoor(aux);
			}
			this.setItemsInRooms();
} catch (Exception e) {
	throw new WrongMapFormatException(e);
}
		return _mapa;
	}

	private static String loadInputStreamInString(InputStream file)
			throws IOException

	{
		String result = "";

		byte[] b = new byte[file.available()];
		// hacemos un array de bytes del mismo tam. que los bytes del fichero.
		file.read(b);// Pues llenamos el array de bytes con los bytes del fichero
		result = new String(b); // lo pasamos a String
		return result;
	}

	private void mapIndicatorSetter() throws WrongMapFormatException
	{

		String indicator;
		StringTokenizer tkn;
		for (int i = 0; i < this._lineas.length; i++)
		{
			indicator = this._lineas[i];
			tkn=new StringTokenizer(indicator);//added indicator var
			indicatorSetter(tkn.nextToken(), i);
		}
		this.areIndicatorsCorrect();
	}

	private void indicatorSetter(String indicator, int line)
			throws WrongMapFormatException
	{

		switch (indicator)
		{

		case "BeginMap":

			if (line != 0)
				throw new WrongMapFormatException();
			break;
		case "BeginRooms":
			if (line != 1)
				throw new WrongMapFormatException();

			break;
		case "EndRooms":
			if (this._EndRooms != -1)
				throw new WrongMapFormatException();
			this._EndRooms = line;

			break;

		case "BeginDoors":
			if (this._BeginDoors != -1)
				throw new WrongMapFormatException();
			this._BeginDoors = line;
			break;
		case "EndDoors":
			if (this._EndDoors != -1)
				throw new WrongMapFormatException();
			this._EndDoors = line;
			break;

		case "BeginItems":
			if (this._BeginItems != -1)
				throw new WrongMapFormatException();
			this._BeginItems = line;
			break;
		case "EndItems":
			if (this._EndItems != -1)
				throw new WrongMapFormatException();
			this._EndItems = line;
			break;
		case "EndMap":
			if (this._EndMap != -1)
				throw new WrongMapFormatException();
			this._EndMap = line;
			break;
		}
	}

	private Door createDoorFromLine(String line, int expectedNumber)
			throws Exception
	{
		int sourceRoom, targetRoom;
		boolean isBidi = false;
		boolean isOpen = false;
		Directions direction = null;
		StringTokenizer tkn = new StringTokenizer(line);
		compara(tkn.nextToken(), "door");
		compara(tkn.nextToken(), String.valueOf(expectedNumber));
		isBidi = tkn.nextToken().equals("bidirectional");// ojo

		compara(tkn.nextToken(), "room");

		sourceRoom = Integer.valueOf(tkn.nextToken());
		direction = Directions.valueOf(tkn.nextToken().toUpperCase());
		compara(tkn.nextToken(), "room");
		targetRoom = Integer.valueOf(tkn.nextToken());

		isOpen = tkn.nextToken().equals("open");// ojo

		return new Door(this._arrayRooms[sourceRoom], direction,
				this._arrayRooms[targetRoom], isBidi, isOpen);
	}

	private void setItemInRoomFromLine(String line, int expectedNumber)
			throws Exception
	{

		String clase = null; // variables comunes
		String id = null;
		String description = null;
		StringTokenizer tkn = new StringTokenizer(line);

		clase = tkn.nextToken();
		itemPermitido(clase);
		compara(tkn.nextToken(), String.valueOf(expectedNumber));
		id = tkn.nextToken();
		
		description = tkn.nextToken();
		if (description.startsWith("\"")) {
			description=description.substring(1);
			do {
				description+=" "+tkn.nextToken();
			}while (!description.endsWith("\""));
		//	description=description.substring(0,description.length()-1);
		}
		else description=description.replaceAll("_", " ");
		this.setItemInRoom(clase, id, description, tkn);

	}

	private void setItemsInRooms() throws Exception
	{
		int expectedNumber = 0;
		for (int i = this._BeginItems + 1; i < this._EndItems; i++)
			this.setItemInRoomFromLine(this._lineas[i], expectedNumber++);

	}

	private void fillArrayRooms() throws Exception
	{
		int expectedNumber = 0;
		this._arrayRooms = new Room[this._EndRooms - this._BeginRooms];

		for (int i = this._BeginRooms + 1; i < this._EndRooms; i++)
			this._arrayRooms[expectedNumber] = createRoomFromLine(this._lineas[i],
					expectedNumber++);

	}


	private Room createRoomFromLine(String line, int expectedNumber)
			throws Exception
	{

		String description = null;
		boolean isExit = false;
		StringTokenizer tkn = new StringTokenizer(line);
		compara(tkn.nextToken(), "room");
		compara(tkn.nextToken(), String.valueOf(expectedNumber));

		String a= tkn.nextToken();// ojo

		description = tkn.nextToken();
		if (description.startsWith("\"")) {
			description=description.substring(1);
			do {
				description+=" "+tkn.nextToken();
			}while (!description.endsWith("\""));
			description=description.substring(0,description.length()-1);
		}
		else description=description.replaceAll("_", " ");
		isExit = !tkn.nextToken().equals("noexit");

		return new Room(isExit, description,a);

	}

	private void compara(String a, String b) throws Exception
	{
		if (!a.equals(b))
			throw new Exception("diferencia: "+a+" "+b);
	}

	private void itemPermitido(String item) throws Exception
	{
		switch (item)
		{
		case "food":
		case "key":
		case "valuable":
			break;
		default:
			throw new Exception();
		}
	}

	private void setItemInRoom(String clase, String id, String description,
			StringTokenizer tkn) throws Exception
	{
		// StringTokenizer tkn = new StringTokenizer(itemData);
		Item item = null;
		switch (clase)
		{
		case "food":
			item = new Food(id, description, Integer.valueOf(tkn.nextToken()),
					Integer.valueOf(tkn.nextToken()));
			break;
		case "key":
			compara(tkn.nextToken(), "door");
			Door d=this._mapa.getDoor(this._arrayDoors[Integer.valueOf(tkn
					.nextToken())]);
			
			item = new Key(id, description,d );
			break;
		case "valuable":
			item = new Valuable(id, description, Integer.valueOf(tkn.nextToken()));
			break;
		default:
			throw new Exception();
		}
		compara(tkn.nextToken(), "room");
		int roomNumber = Integer.valueOf(tkn.nextToken());
		this._arrayRooms[roomNumber].addItem(item);

	}

	private void areIndicatorsCorrect() throws WrongMapFormatException
	{

		if (!(this._BeginMap < this._BeginRooms
				&& this._BeginRooms < this._EndRooms
				&& this._EndRooms < this._BeginDoors
				&& this._BeginDoors < this._EndDoors
				&& this._EndDoors < this._BeginItems
				&& this._BeginItems < this._EndItems && this._EndItems < this._EndMap))
		//	throw new WrongMapFormatException();
return;
	}
}